﻿#region Copyright

// All Material Copyright © 2010 Dynamic Concepts Development Corp., All Rights Reserved.
//      Portions Copyright © 1984-2009 Dynamic Concepts Development Corp., All Rights Reserved.
// For further information please contact: SoftwareSupport@dynconcepts.com
// Dynamic Concepts Development Corp.  311 W 57th St. Suite #422, New York, NY 10019

#endregion

#region Using References

using System;
using System.Threading;
using System.Windows.Threading;
using DynConDevCorp.OpenArch.SupportLibrary.ApplicationInfrastructure;

#endregion

namespace DynConDevCorp.OpenArch.SupportLibrary.ParallelExecution.Events
{
    internal class ParallelEventTarget<TARGS> : IParallelEventTarget where TARGS : EventArgs
    {
        #region Constructors

        internal ParallelEventTarget(ParallelEventHandler<TARGS> internalHandler, ParallelEventHandler<TARGS> completionHandler, Dispatcher eventCreatorDispatcher, string name, IParallelEvent firingEvent, ParallelEventDispatchMode dispatchMode)
        {
            if (m_EnableLogging) QueuedLogger.GlobalInstance.Log(QueuedLogger.MessageSeverity.Debug, "ParallelEvent: {0}:{1} - Created!", ID, name);
            m_Name = name;
            r_EventTargetDispatcher = Dispatcher.CurrentDispatcher;
            r_CompletionHandler = completionHandler;
            r_IsCrossThreadTarget = !ReferenceEquals(r_EventTargetDispatcher, eventCreatorDispatcher);
            r_DispatchPriority = DispatcherPriority.Send;
            r_InternalHandler = internalHandler;
            r_FiringEvent = firingEvent;
            m_DispatchMode = dispatchMode;
        }

        #endregion

        #region Public Properies and Indexers

        public ParallelEventDispatchMode DispatchMode
        {
            get { return m_DispatchMode; }
            set { m_DispatchMode = value; }
        }

        public string Name
        {
            get
            {
                if (String.IsNullOrEmpty(m_Name))
                    return r_ID.ToString();
                else
                    return m_Name;
            }
            set { m_Name = value; }
        }

        #endregion

        #region Internal Properties and Indexers

        internal Guid ID
        {
            get { return r_ID; }
        }

        internal bool IsCrossThreadTarget
        {
            get { return r_IsCrossThreadTarget; }
        }

        #endregion

        #region Internal Methods

        internal void ExecuteInternalHandler(object sender, TARGS args)
        {
            if (m_EnableLogging) QueuedLogger.GlobalInstance.Log(QueuedLogger.MessageSeverity.Debug, "ParallelEventTarget({0}).ExecuteInternalHandler()", Name);

            if (!ReferenceEquals(r_FiringEvent.EventCreatorDispatcher, Dispatcher.CurrentDispatcher))
            {
                if (m_EnableLogging) QueuedLogger.GlobalInstance.Log(QueuedLogger.MessageSeverity.Debug, "ParallelEventTarget({0}).ExecuteInternalHandler() - Invoked on WRONG THREAD!", Name);
            }

            bool direct = (r_IsCrossThreadTarget == false);
            direct |= (m_DispatchMode == ParallelEventDispatchMode.CrossThreadDirect);
            direct &= (m_DispatchMode != ParallelEventDispatchMode.ForceAsync);
            if (direct)
            {
                InvokeInternalHandler(sender, args);
            }
            else
            {
                Dispatcher targetDispatcher = r_EventTargetDispatcher;
                if ((m_DispatchMode == ParallelEventDispatchMode.ForceAsync) && ReferenceEquals(r_EventTargetDispatcher, Dispatcher.CurrentDispatcher))
                {
                    targetDispatcher = GetBackgroundDispatcher();
                }
                InvokeOnThread(targetDispatcher, sender, args, m_DispatchMode);
            }
        }

        internal void InvokeOnThread(Dispatcher dispatcher, object sender, TARGS args, ParallelEventDispatchMode dispatchMode)
        {
            if (m_EnableLogging) QueuedLogger.GlobalInstance.Log(QueuedLogger.MessageSeverity.Debug, "ParallelEventTarget({0}).InvokeOnThread({1}) - Started", Name, dispatchMode);
            switch (dispatchMode)
            {
                case ParallelEventDispatchMode.VirtualSync:
                    VirtualSyncEventHandlerExecutionContext<TARGS>.Execute(r_FiringEvent, dispatcher, InvokeInternalHandler, sender, args);
                    break;
                case ParallelEventDispatchMode.AutoAsync:
                    PureAsyncEventHandlerExecutionContext<TARGS>.Execute(r_FiringEvent, dispatcher, InvokeInternalHandler, sender, args);
                    break;
                case ParallelEventDispatchMode.ForceAsync:
                    PureAsyncEventHandlerExecutionContext<TARGS>.Execute(r_FiringEvent, dispatcher, InvokeInternalHandler, sender, args);
                    break;
            }
            if (m_EnableLogging) QueuedLogger.GlobalInstance.Log(QueuedLogger.MessageSeverity.Debug, "ParallelEventTarget({0}).InvokeOnThread() - Completed", Name);
        }

        internal bool Matches(ParallelEventHandler<TARGS> otherHandler)
        {
            return r_InternalHandler.Equals(otherHandler);
        }

        #endregion

        #region Private Methods

        private static void ApplicationThreadCreated(object sender, EventArgs args)
        {
            s_BackgroundDispatcherThread = (ParallelHostThread) sender;
            s_BackgroundThreadStartSemaphore.Release();
        }

        private static void CreateApplicationThread()
        {
            ParallelHostThread.CreateNew(null, ApplicationThreadCreated);
        }

        private void ExecuteCompletionHandler(object sender, TARGS args)
        {
            if (m_EnableLogging) QueuedLogger.GlobalInstance.Log(QueuedLogger.MessageSeverity.Debug, "ParallelEventTarget({0}).ExecuteCompletionHandler()", Name);
            if (!r_IsCrossThreadTarget) // All items on the same thread are synchronous!
            {
                InvokeCompletionHandler(sender, args);
            }
            else
            {
                r_EventTargetDispatcher.BeginInvoke(r_DispatchPriority, new CrossThreadExecuteHandler(InvokeCompletionHandler), sender, args);
            }
        }

        private static Dispatcher GetBackgroundDispatcher()
        {
            lock (rs_SyncRoot)
            {
                if (s_BackgroundDispatcherThread == null)
                {
                    s_BackgroundThreadStartSemaphore = new Semaphore(0, 1);
                    Thread newThread = new Thread(CreateApplicationThread);
                    newThread.Name = "Background Event Thread";
                    newThread.Start();
                    s_BackgroundThreadStartSemaphore.WaitOne();
                    s_BackgroundThreadStartSemaphore = null;
                }
                return s_BackgroundDispatcherThread.Dispatcher;
            }
        }

        private void InvokeCompletionHandler(object sender, TARGS args)
        {
            r_CompletionHandler.Execute(sender, args);
        }

        private void InvokeInternalHandler(object sender, TARGS args)
        {
            r_InternalHandler.Execute(sender, args);
            if (r_CompletionHandler != null)
            {
                ExecuteCompletionHandler(sender, args);
            }
            if (r_FiringEvent != null)
            {
                if (r_FiringEvent.ArgumentType == typeof (TARGS))
                {
                    r_FiringEvent.TargetCompleted(this, sender, args);
                }
                else
                {
                    r_FiringEvent.TargetCompleted(this, sender, null); // HACK!
                }
            }
        }

        #endregion

        #region Static Fields and Constants

        private const bool c_EnableLogging = false;

        private static readonly Object rs_SyncRoot = new Object();
        private static ParallelHostThread s_BackgroundDispatcherThread;
        private static Semaphore s_BackgroundThreadStartSemaphore;

        #endregion

        #region Instance Fields (ReadOnly)

        private readonly ParallelEventHandler<TARGS> r_CompletionHandler;
        private readonly DispatcherPriority r_DispatchPriority;
        private readonly Dispatcher r_EventTargetDispatcher;
        private readonly IParallelEvent r_FiringEvent;
        private readonly Guid r_ID = Guid.NewGuid();
        private readonly ParallelEventHandler<TARGS> r_InternalHandler;
        private readonly Boolean r_IsCrossThreadTarget;

        #endregion

        #region Instance Fields (Mutable)

        private ParallelEventDispatchMode m_DispatchMode;
        private bool m_EnableLogging = c_EnableLogging;
        private string m_Name;

        #endregion

        #region Nested type: CrossThreadExecuteHandler

        private delegate void CrossThreadExecuteHandler(object sender, TARGS args);

        #endregion
    }
}